#pragma once

#include "sfc/math.h"

namespace sfc::math::geo {

static constexpr f64 EPSILON = 1e-10;

template <class T>
struct Vec2 {
  T _inn[2] = {0, 0};

 public:
  [[sfc_inline]] auto operator[](usize idx) const -> const T& {
    return _inn[idx];
  }

  [[sfc_inline]] auto operator[](usize idx) -> T& {
    return _inn[idx];
  }

  // (x, -y)
  [[sfc_inline]] auto conj() const -> Vec2 {
    return Vec2{_inn[0], -_inn[1]};
  }

  // sqrt(x^2+y^2)
  [[sfc_inline]] auto length() const -> T {
    return math::hypot(_inn[0], _inn[1]);
  }

  // x^2+y^2
  [[sfc_inline]] auto length_sqr() const -> T {
    return _inn[0] * _inn[0] + _inn[1] * _inn[1];
  }

  // |x|+|y|
  [[sfc_inline]] auto norm1() const -> T {
    return math::fabs(_inn[0]) + math::fabs(_inn[1]);
  }

  // sqrt(x^2+y2)
  [[sfc_inline]] auto norm2() const -> T {
    return math::hypot(_inn[0], _inn[1]);
  }

  // unit vector
  [[sfc_inline]] auto to_unit() const -> Vec2 {
    const auto s = this->length();
    if (s < EPSILON) {
      return Vec2{1.0, 0.0};
    }
    return Vec2{_inn[0] / s, _inn[1] / s};
  }

  [[sfc_inline]] auto operator==(const Vec2& other) const -> bool {
    return _inn[0] == other._inn[0] && _inn[1] == other._inn[1];
  }

  [[sfc_inline]] auto operator-() const -> Vec2 {
    return Vec2{-_inn[0], -_inn[1]};
  }

  [[sfc_inline]] auto operator+(Vec2 v) const -> Vec2 {
    return Vec2{_inn[0] + v._inn[0], _inn[1] + v._inn[1]};
  }

  [[sfc_inline]] auto operator-(Vec2 v) const -> Vec2 {
    return Vec2{_inn[0] - v._inn[0], _inn[1] - v._inn[1]};
  }

  [[sfc_inline]] auto operator/(T k) const -> Vec2 {
    return Vec2{_inn[0] / k, _inn[1] / k};
  }

  [[sfc_inline]] friend auto operator*(T k, const Vec2& v) -> Vec2 {
    return v.scale(k);
  }

  [[sfc_inline]] auto operator*(const Vec2& b) const -> T {
    return _inn[0] * b._inn[0] + _inn[1] * b._inn[1];
  }

  [[sfc_inline]] auto scale(T k) const -> Vec2 {
    return Vec2{k * _inn[0], k * _inn[1]};
  }

  [[sfc_inline]] auto dot_prod(const Vec2& b) const -> T {
    return _inn[0] * b._inn[0] + _inn[1] * b._inn[1];
  }

  [[sfc_inline]] auto cross_prod(const Vec2& b) const -> T {
    return _inn[0] * b._inn[1] - _inn[1] * b._inn[0];
  }

  void fmt(auto& f) const {
    f.write(_inn);
  }
};

template <class T>
Vec2(T, T) -> Vec2<T>;

using Vec2d = Vec2<f64>;

}  // namespace sfc::math::geo
